from utils.serializers import BaseSerializer, BaseListPageSerializer
import pandas as pd
import pymysql
import time
import random
import sys
import math
from operator import itemgetter
from collections import Counter
import datetime
random.seed(0)



class ItemCF(object):
    def __init__(self, appId):
        self.trainset = {}
        self.testset = {}
        self.buyset = {}

        self.n_sim_movie = 20

        self.movie_sim_mat = {}
        self.movie_popular = {}
        self.movie_count = 0
        self.appId = appId

        print('Similar movie number = %d' % self.n_sim_movie, file=sys.stderr)

    def data_process(self, train_date, records):
        self.records = records
        # timeArray_train = time.strptime(train_date, "%Y-%m-%d")
        # otherStyleTime = time.strftime("%Y-%m-%d", timeArray_train)
        train_date = datetime.datetime.strptime(train_date, '%Y-%m-%d')
        self.train_date = train_date.date()
        delta = datetime.timedelta(days=30)
        begin_date = train_date - delta

        host = '127.0.0.1'
        user = 'root'
        password = '123456'
        port = 3306

        mysql = pymysql.connect(host=host, user=user, password=password, port=port)
        cursor = mysql.cursor()
        sql = 'select userId, objectId, visitTime, appId from rec_track.rec_profile'
        cursor.execute(sql)
        results = cursor.fetchall()  # 用于返回多条数据
        df1 = pd.DataFrame(list(results), columns=['userId', 'objectId', 'visitTime', 'appId'])
        df1.dropna(axis=0, how='any', inplace=True)
        df1 = df1[df1['appId'] == self.appId]
        df1['visitTime'] = df1['visitTime'].apply(lambda x: int(x))
        df1['timeArray'] = df1['visitTime'].apply(lambda x: time.localtime(x/1000))
        df1['time'] = df1['timeArray'].apply(lambda x: time.strftime("%Y-%m-%d", x))

        df1['time1'] = pd.to_datetime(df1['time']).dt.date
        df1['sub_days'] = (self.train_date - df1['time1']).dt.days
        df1['weight'] = 1.0 / (1 + 0.3 * df1['sub_days'])
        df1 = df1[['time', 'objectId', 'userId', 'weight']]
        # date = '2020-12-' + self.number
        train = df1[(df1['time'] >= begin_date.strftime('%Y-%m-%d')) & (df1['time'] < train_date.strftime('%Y-%m-%d'))].values.tolist()
        self.train = df1[(df1['time'] >= begin_date.strftime('%Y-%m-%d')) & (df1['time'] < train_date.strftime('%Y-%m-%d'))]
        trainset_len = 0
        for i in train:
            user, item, score = i[2], i[1], i[3]
            self.trainset.setdefault(user, {})
            self.trainset[user][item] = float(score)
            trainset_len += 1

        # 推荐时排除购买的产品
        # df = pd.read_csv(file_buy)
        # print(df)
        # df.dropna(inplace=True)
        # df = df.loc[(df['state'] == 'DELIVERED') | (df['state'] == 'ARRVIED') | (df['state'] == 'FINISHED')]
        # df.reset_index(inplace=True, drop=True)
        # print(df)
        # df['time'] = df['stime'].apply(lambda x: time.strptime(x, "%d/%m/%Y %H:%M:%S"))
        # df['time'] = df['time'].apply(lambda x: time.strftime("%Y-%m-%d", x))
        # df = df[(df['time'] >= '2020-12-01') & (df['time'] < '2020-12-28')]
        # df.drop('tradeId', inplace=True, axis=1)
        # buy_set = df[['buyerId', 'itemId']].values.tolist()
        # for i in buy_set:
        #     user, item = i[0], i[1]
        #     self.buyset.setdefault(user, [])
        #     self.buyset[user].append(item)

    def calc_movie_sim(self):
        for user, movies in self.trainset.items():
            for movie in movies:
                if movie not in self.movie_popular:
                    self.movie_popular[movie] = 0
                self.movie_popular[movie] += movies[movie]
        print('count movies number and pipularity succ', file=sys.stderr)

        self.movie_count = len(self.movie_popular)
        print('total movie number = %d' % self.movie_count, file=sys.stderr)
        self.movie_popular_order = sorted(self.movie_popular.items(), key=lambda x: x[1], reverse=True)
        self.movie_most_popular = self.movie_popular_order[: self.records]
        itemsim_mat = self.movie_sim_mat
        print('building co-rated users matrix', file=sys.stderr)
        for user, movies in self.trainset.items():
            for m1 in movies:
                for m2 in movies:
                    if m1 == m2:
                        continue
                    itemsim_mat.setdefault(m1, {})
                    itemsim_mat[m1].setdefault(m2, 0)
                    itemsim_mat[m1][m2] += 1

        print('build co-rated users matrix succ', file=sys.stderr)
        print('calculating movie similarity matrix', file=sys.stderr)

        simfactor_count = 0
        PRINT_STEP = 2000000

        for m1, related_movies in itemsim_mat.items():
            for m2, count in related_movies.items():
                itemsim_mat[m1][m2] = count / math.sqrt(self.movie_popular[m1] * self.movie_popular[m2])
                simfactor_count += 1
                if simfactor_count % PRINT_STEP == 0:
                    print('calcu movie similarity factor(%d)' % simfactor_count, file=sys.stderr)
        self.movie_sim_mat = itemsim_mat


        print('calcu similiarity succ', file=sys.stderr)

        # self.hot_out = []
        # for movie, _ in self.movie_most_popular:
        #     self.hot_out.append(movie)

        self.hot_out = self.movie_most_popular
        final_out_dic = []
        for item in self.hot_out:
            final_out_dic.append({'objectId': item[0], 'score': item[1]})

        return final_out_dic

    def rule1(self, train_list):
        dict = Counter(train_list)
        if len(set(train_list)) > self.records:
            return [x[0] for x in dict.most_common(self.records)]
        else:
            return [x[0] for x in dict.most_common(len(set(train_list)))]



    def recommend(self, user):
        K = self.n_sim_movie
        N = self.records
        rank = {}
        if user in self.trainset.keys():
            watched_movies = self.trainset[user]
            for movie, rating in watched_movies.items():
                if movie in self.movie_sim_mat.keys():   # 判断用户浏览过的产品未出现在物品相似度矩阵中，若未出现则令对产品进行推荐
                    # 物品相似度矩阵下某个物品的相关产品较小
                    if len(self.movie_sim_mat[movie].items()) < K:
                        K = len(self.movie_sim_mat[movie].items())

                    less_train = self.rule1(watched_movies)
                    if less_train:
                        for ite in less_train:
                            rank[ite] = 10000

                    for related_movie, similarity_factor in sorted(self.movie_sim_mat[movie].items(), key=itemgetter(1),
                                                                   reverse=True)[0:K]:
                        if related_movie in watched_movies:
                            continue
                        rank.setdefault(related_movie, 0)
                        rank[related_movie] += similarity_factor * rating
                else:
                    rank[movie] = 10000

            # 推荐时排除购买过的产品
            # if user in self.buyset.keys():
            #     buy_items = self.buyset[user]
            #     for buy_item in buy_items:
            #         if buy_item in rank.keys():
            #             rank.pop(buy_item)

            return sorted(rank.items(), key=itemgetter(1), reverse=True)[0:N]

        else:
            return self.movie_most_popular

    def evaluate(self, user):
        print('evaluation start', file=sys.stderr)
        final_out = self.recommend(user)
        # final_out = []
        # for movie, _ in rec_movies:
        #     final_out.append(movie)
        if len(final_out) < self.records:
            for i in range(self.records - len(final_out)):
                final_out.append(self.hot_out[i])

        final_out_dic = []
        for item in final_out:
            final_out_dic.append({'objectId': item[0], 'score': item[1]})

        return final_out_dic




class UserProfileSerializer(BaseSerializer):
    """ 用户的详细信息 """
    def to_dict(self):
        profile = self.obj
        return {
            'success': True,
            'code': '',
            'message': '',
        }

class UserResultSerializer(BaseSerializer):
    """ 用户的详细信息 """
    def to_dict(self, item_cf_list):
        profile = self.obj
        print('profile:', profile)
        user = profile['userId']
        item_cf = item_cf_list[profile['appId']]
        out = item_cf.evaluate(user)

        return {
            'success': True,
            'data': out,
            'code': '',
            'message': '',
        }

class UserHotSerializer(BaseSerializer):
    """ 用户的详细信息 """
    def to_dict(self, item_cf_list):
        profile = self.obj
        print('profile:', profile)
        if profile['to_train'] == 1:
            train_date = profile['train_date']
            records = profile['records']
            item_cf = ItemCF(profile['appId'])
            item_cf.data_process(train_date, records)
            hot_items = item_cf.calc_movie_sim()
            item_cf_list[profile['appId']] = item_cf

        return {
            'success': True,
            'hot_items': hot_items,
            'code': '',
            'message': '',
        }

class OutProfileSerializer(BaseSerializer):
    """ 用户的详细信息 """
    def to_dict(self):
        profile = self.obj
        return {
            'success': True,
            'code': '',
            'message': '',
        }